﻿
using Boilen.Primitives.Members;
using System;


namespace Boilen.Primitives.Implementers {

    /// <summary>
    /// Implements a property.
    /// </summary>
    /// <typeparam name="T">The type of the property.</typeparam>
    public abstract class Property<T> : ValueAccessor<T> {

        /// <inheritdoc/>
        protected Property( PartialType parent, string name, string description )
            : base( parent, name, description ) {
        }


        #region Standard Guards

        /// <summary>
        /// Ensures the assigned property value is not <null/>.
        /// </summary>
        public Property<T> NotNull( Action<Guard> configure = null ) {
            this.AddGuard( Guard.NotNull( this.CreateDoc( ), this.Name, this.Parent.IsInternal ), configure );
            return this;
        }

        /// <summary>
        /// Ensures the assigned <see cref="Enum"/> property value is defined.
        /// </summary>
        public Property<T> EnumIsDefined( Action<Guard> configure = null ) {
            Ensure.Satisfies( typeof( T ).IsEnum, "Property type {0} is not an enum type.", typeof( T ) );
            this.AddGuard( Guard.EnumIsDefined( this.CreateDoc( ), this.Name, this.Parent.IsInternal ), configure );
            return this;
        }

        /// <summary>
        /// Creates a guard to ensure an <see cref="double"/> value is not a special value.
        /// </summary>
        public Property<T> NotSpecialValue( Action<Guard> configure = null ) {
            this.AddGuard( Guard.NotSpecialValue( this.CreateDoc( ), this.Name, this.Parent.IsInternal ), configure );
            return this;
        }

        /// <summary>
        /// Ensures the assigned property value is within the range defined by the specified condition.
        /// </summary>
        public Property<T> IsInRange( string description, Action<Guard> configure, string condition, string format, params string[] args ) {
            this.AddGuard( Guard.IsInRange( this.CreateDoc( ), this.Name, description, condition, format, args ), configure );
            return this;
        }
        public Property<T> IsInRange( string description, string condition, string format, params string[] args ) { return this.IsInRange( description, null, condition, format, args ); }
        public Property<T> IsInRange( Action<Guard> configure, string condition, string format, params string[] args ) { return this.IsInRange( null, configure, condition, format, args ); }
        public Property<T> IsInRange( string condition, string format, params string[] args ) { return this.IsInRange( null, null, condition, format, args ); }

        /// <summary>
        /// Ensures the assigned property value satisfies the requirements defined by the specified condition.
        /// </summary>
        public Property<T> Satisfies( string description, Action<Guard> configure, string condition, string format, params string[] args ) {
            this.AddGuard( Guard.Satisfies( this.CreateDoc( ), this.Name, description, condition, format, args ), configure );
            return this;
        }
        public Property<T> Satisfies( string description, string condition, string format, params string[] args ) { return this.Satisfies( description, null, condition, format, args ); }
        public Property<T> Satisfies( Action<Guard> configure, string condition, string format, params string[] args ) { return this.Satisfies( null, configure, condition, format, args ); }
        public Property<T> Satisfies( string condition, string format, params string[] args ) { return this.Satisfies( null, null, condition, format, args ); }

        #endregion

    }

}
